﻿using System;
using UnityEngine.Events;
using UnityEngine.EventSystems;
using UnityEngine;
using UnityEngine.UI;
using System.Collections;
using System.Collections.Generic;
using UnityEngine.SocialPlatforms;
using XLua.LuaDLL;
using XLua;

/// <summary>
/// author: 刘时印
/// des ：可复用ScrollView
/// </summary>
public class CommonScrollView : MonoBehaviour
{
    public ScrollRect innerScrollRect;
    public RectTransform viewPort;
    // 内容区域
    public RectTransform innerContent;
    // item模板
    public RectTransform itemTemplate;
    private RectTransform rectTransform;
    // item间距
    public float itemSpace = 5;
    // 滚动方向
    public bool isVertical = true;
    private int count = 0;

    class ItemData
    {
        public int index = 0;
        public RectTransform item = null;
        public LuaBehaviour luaBehaviour;
    }
    private List<ItemData> itemCaches = new List<ItemData>();
    private List<ItemData> itemList = new List<ItemData>();
    // 
    private LuaFunction updateCallback = null;
    private LuaFunction valueChangeCallback = null;
    private LuaFunction customCallback = null;

    private void Awake()
    {
        InitListView();
    }

    public void SetTemplate(LuaFunction luaUpdate = null, LuaFunction luaValueChange = null, LuaFunction customCallback = null)
    {
        updateCallback = luaUpdate;
        valueChangeCallback = luaValueChange;
        this.customCallback = customCallback;
    }

    void InitListView()
    {
        rectTransform = GetComponent<RectTransform>();
        innerScrollRect = GetComponent<ScrollRect>();
        Transform viewPortObj = transform.Find("Viewport");
        viewPort = viewPortObj.GetComponent<RectTransform>();
        innerContent = viewPortObj.Find("Content").GetComponent<RectTransform>();

        innerScrollRect.content.anchorMax = new Vector2(0, 1);
        innerScrollRect.content.anchorMin = new Vector2(0, 1);
        innerScrollRect.content.pivot = new Vector2(0, 1);

        SetInnerContentPositionY(0);
        SetInnerContentPositionX(0);

        ScrollRect.ScrollRectEvent scrollEvent = new ScrollRect.ScrollRectEvent();
        scrollEvent.AddListener(OnValueChange);
        innerScrollRect.onValueChanged = scrollEvent;

        innerContent.anchorMin = new Vector2(0, 1);
        innerContent.anchorMax = new Vector2(0, 1);
        innerContent.pivot = new Vector2(0, 1);

        SetDirection(isVertical);
    }

    public bool IsVertical
    {
        set
        {
            SetDirection(isVertical);
        }
        get
        {
            return isVertical;
        }
    }

    private void CalcStartIndexAndEndIndex()
    {
        var innerPos = innerContent.localPosition;
        var itemRect = itemTemplate.rect;
        var contentSize = rectTransform.rect;

        int startIndex = 0, endIndex = 0;
        if (isVertical)
        {
            startIndex = (int)Mathf.Ceil(innerPos.y / (itemRect.height + itemSpace));
            startIndex = startIndex - 1 < 0 ? 0 : startIndex - 1;
            endIndex = startIndex + (int)Mathf.Ceil(contentSize.height / (itemRect.height + itemSpace));
            endIndex = endIndex + 1 > count - 1 ? count - 1 : endIndex + 1;
        }
        else
        {
            startIndex = (int)Mathf.Ceil(-innerPos.x / (itemRect.width + itemSpace));
            startIndex = startIndex - 1 < 0 ? 0 : startIndex - 1;
            endIndex = startIndex + (int)Mathf.Ceil(contentSize.width / (itemRect.width + itemSpace));
            endIndex = endIndex + 1 > count - 1 ? count - 1 : endIndex + 1;
        }
        var itemArray = itemList.ToArray();
        for (int i = 0; i < itemArray.Length; ++i)
        {
            if (itemArray[i].index < startIndex || itemArray[i].index > endIndex)
            {
                ReToCaches(ref itemArray[i]);
                itemList.Remove(itemArray[i]);
            }
        }
        for (int index = startIndex; index <= endIndex; ++index)
        {
            if (!InContainsInItemList(index))
            {
                AddItem(index);
            }
        }
    }    

    bool InContainsInItemList(int index)
    {
        foreach(var cell in itemList)
        {
            if(cell.index==index)
            {
                return true;
            }
        }
        return false;
    }

    void OnValueChange(Vector2 delta)
    {
        CalcStartIndexAndEndIndex();
        if(valueChangeCallback!=null)
        {
            valueChangeCallback.Call(delta);
        }
    }


    public void SetDirection(bool isVertical)
    {
        this.isVertical = isVertical;
        innerScrollRect.vertical = isVertical;
        innerScrollRect.horizontal = !isVertical;
    }

    void SetInnerContentPositionY(float y)
    {
        var prePos = innerContent.localPosition;
        prePos.y = y;
        innerContent.localPosition = prePos;
    }

    void SetInnerContentPositionX(float x)
    {
        var prePos = innerContent.localPosition;
        prePos.x = x;
        innerContent.localPosition = prePos;
    }

    Vector2 GetItemPosition(int index)
    {
        Vector2 itemPos = new Vector2(0, 0);
        if (isVertical)
        {
            itemPos.y = -(index) * (itemTemplate.rect.height + itemSpace);
        }
        else
        {
            itemPos.x = (index) * (itemTemplate.rect.width + itemSpace);
        }
        return itemPos;
    }

    public Vector2 GetInnerContentSize()
    {
        return GetRectTranformSize(innerContent);
    }

    public Vector3 GetInnerContentPosition()
    {
        return innerContent.localPosition;
    }

    public Vector2 GetContentSize()
    {
        return GetRectTranformSize(rectTransform);
    }

    Vector2 GetRectTranformSize(RectTransform tran)
    {
        Rect itemRect = tran.rect;
        return new Vector2(Mathf.Abs(itemRect.xMax - itemRect.xMin), Mathf.Abs(itemRect.yMax - itemRect.yMin));
    }

    public void ReSize(int nums)
    {
        if (itemTemplate == null)
        {
            return;
        }
        count = nums;
        ClearItem();
        CalcStartIndexAndEndIndex();
        SetInnerContentSize(CalculateInnerContentSize(nums));
    }

    void ClearItem()
    {
        foreach (var item in itemList)
        {
            Destroy(item.item.gameObject);
        }
        itemList.Clear();
    }

    void CreateCacheItem(int index)
    {
        ItemData itemData = CreateItem(index);
        itemData.item.gameObject.SetActive(false);
        itemData.item.localPosition = new Vector3(-9999, -9999);
        itemCaches.Add(itemData);
    }

    void AddItem(int index)
    {
        var itemData = GetCacheItem();
        itemData.item.localPosition = GetItemPosition(index);
        itemData.item.gameObject.SetActive(true);
        itemData.index = index;
        OnUpdateItem(itemData, index);
        itemList.Add(itemData);
    }

    public void UpdateUsedCell()
    {
        foreach (var item in itemList)
        {
            OnUpdateItem(item, item.index);
        }
    }

    ItemData GetCacheItem()
    {
        var cacheArray = itemCaches.ToArray();
        if (cacheArray.Length == 0)
        {
            ReSpawnCaches();
            cacheArray = itemCaches.ToArray();
        }
        ItemData result = cacheArray[0];
        itemCaches.RemoveAt(0);
        return result;
    }

    void ReToCaches(ref ItemData itemData)
    {
        itemData.item.gameObject.SetActive(false);
        itemData.item.localPosition = new Vector3(-9999, -9999);
        itemCaches.Add(itemData);
    }

    void ReSpawnCaches()
    {
        //var templateSize = GetRectTranformSize(itemTemplate);
        //int nums = 0;
        //if (isVertical)
        //{
        //    nums = (int)Mathf.Ceil(GetContentSize().y / templateSize.y);
        //}
        //else
        //{
        //    nums = (int)Mathf.Ceil(GetContentSize().x / templateSize.x);
        //}
        for (int i = 0; i < 3; ++i)
        {
            CreateCacheItem(i);
        }
    }

    ItemData CreateItem(int index)
    {
        ItemData itemData = new ItemData();
        RectTransform item = Instantiate<RectTransform>(itemTemplate, innerContent.transform);
        item.anchorMin = new Vector2(0f, 1);
        item.anchorMax = new Vector2(0f, 1);
        item.pivot = new Vector2(0, 1);
        item.name = item.name + index;
        itemData.item = item;
        itemData.index = index;
        itemData.luaBehaviour = item.GetComponent<LuaBehaviour>();
        if (itemData.luaBehaviour != null)
        {
            var scriptEnv = itemData.luaBehaviour.GetScriptEnv();
            if (scriptEnv != null)
            {
                scriptEnv.Set("customCallback", this.customCallback);
            }
        }
        return itemData;
    }

    void OnUpdateItem(ItemData itemData, int index)
    {
        if(updateCallback==null)
        {
            return;
        }
        var luaBehaviour = itemData.luaBehaviour;
        if (luaBehaviour != null)
        {
            updateCallback.Call(luaBehaviour.GetScriptEnv(), index);
        }
        else
        {
            updateCallback.Call(itemData.item, index);
        }
    }

    Vector2 CalculateInnerContentSize(int nums)
    {
        Vector2 itemSize = GetRectTranformSize(itemTemplate);
        Vector2 contentSize = GetContentSize();
        var innerSize = new Vector2();
        if (isVertical)
        {
            innerSize.x = contentSize.x;
            innerSize.y = itemSize.y * nums + itemSpace * nums;
        }
        else
        {
            innerSize.x = itemSize.x * nums + itemSpace * nums;
            innerSize.y = contentSize.y;
        }
        return innerSize;
    }

    void SetInnerContentSize(Vector2 size)
    {
        innerContent.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, size.y);
        innerContent.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, size.x);
        if (isVertical)
        {
            SetInnerContentPositionY(0);
        }
        else
        {
            SetInnerContentPositionX(0);
        }
    }
}
